Dynamic wakes

For spatially uniform timeseries input data, foxes can compute dynamic wake propagation. This in principle works by following a flow trace backwards in time for each point of interest, and identifying it with a wake trajectory if it hits a rotor.

Since all foxes computations are based on chunks of input states, this concept only works if

  • either all states fall into a single chunk,

  • or the Iterative algorithm is used for the calculation.

The later is necessary in case the wake originates from a state previous to the chunk of evaluation, since the default Downwind algorithm does not allow cross-chunk communication during the calculation.

These are the inlcudes for this example:

In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["animation.html"] = "jshtml"

import foxes
import foxes.variables as FV
import foxes.constants as FC

We create a case with a regular 3 x 3 wind farm layout:

In [2]:
mbook = foxes.models.ModelBook()

states = foxes.input.states.Timeseries(
    data_source="timeseries_100.csv.gz",
    output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
    var2col={FV.WS: "ws", FV.WD: "wd", FV.TI: "ti"},
    fixed_vars={FV.RHO: 1.225, FV.TI: 0.07},
)

farm = foxes.WindFarm()
foxes.input.farm_layout.add_grid(
    farm,
    xy_base=np.array([0.0, 0.0]),
    step_vectors=np.array([[1000.0, 0], [0, 800.0]]),
    steps=(3, 3),
    turbine_models=["DTU10MW"],
    verbosity=0
)

algo = foxes.algorithms.Iterative(
    mbook,
    farm,
    states=states,
    rotor_model="centre",
    wake_models=["Bastankhah_linear_k004"],
    wake_frame="timelines",
    partial_wakes_model="auto",
    chunks={FC.STATE: 500, FC.POINT: 5000},
    verbosity=1
)

Notice the wake frame choice timelines, which is a pre-defined instance of the class Timelines from the model book.

Let’s run the wind farm calculation:

In [3]:
with foxes.utils.runners.DaskRunner() as runner:
    farm_results = runner.run(algo.calc_farm)

Algorithm Iterative: Iteration 0


Initializing algorithm 'Iterative'
Initializing model 'Iterative'
Initializing model 'Timeseries'
States 'Timeseries': Reading static data 'timeseries_100.csv.gz' from context 'states'
Path: /home/jonas/gits/wakes/foxes/foxes/data/states/timeseries_100.csv.gz
Initializing model 'centre'
Initializing model 'basic_ctrl_prer'
Initializing model 'DTU10MW'
Turbine type 'DTU10MW': Reading static data from context 'power_ct_curve'
Path: /home/jonas/gits/wakes/foxes/foxes/data/power_ct_curves/DTU-10MW-D178d3-H119.csv
Initializing model 'basic_ctrl_postr'
Initializing model 'basic_ctrl'
Initializing model 'timelines'
timelines: Pre-calculating ambient wind vectors
Partial wakes 'auto': Applying PartialAxiwake to ['Bastankhah_linear_k004']
Initializing model 'linear'
Initializing model 'Bastankhah_linear_k004'
Initializing model 'PartialAxiwake10'
Initializing model 'auto'

--------------------------------------------------
  Running Iterative: calc_farm
--------------------------------------------------
  n_states : 100
  n_turbines: 9
--------------------------------------------------
  states   : Timeseries (Timeseries)
  rotor    : centre (CentreRotor)
  controller: basic_ctrl (BasicFarmController)
  partialwks: auto (Mapped)
  wake frame: timelines (Timelines)
--------------------------------------------------
  wakes:
    0) Bastankhah_linear_k004 (BastankhahWake)(k=0.04, sp=linear)
--------------------------------------------------
  turbine models:
    0) DTU10MW (PCtFile)
--------------------------------------------------

Initializing model 'SetXYHD'
Initializing model 'SetXYHD_t2f'
Initializing model 'calc_yaw_CentreRotor1'
Initializing model 'CalcOrder'
Initializing model 'SetAmbFarmResults'
Initializing model 'URelax_CT'
Initializing model 'FarmWakesCalculation'
Initializing model 'Iterative_calc'

--------------------------------------------------
  Model oder
--------------------------------------------------
00) SetXYHD_t2f
01) basic_ctrl
02) calc_yaw_CentreRotor1
03) centre
04) CalcOrder
05) basic_ctrl
  05.0) Post-rotor: DTU10MW
06) SetAmbFarmResults
07) FarmWakesCalculation
--------------------------------------------------

[########################################] | 100% Completed | 103.31 ms

Input data:

 <xarray.Dataset>
Dimensions:          (state: 100, turbine: 9, Timeseries_vars: 2, tmodels: 1)
Coordinates:
  * state            (state) datetime64[ns] 2023-07-07T12:00:00 ... 2023-07-0...
  * Timeseries_vars  (Timeseries_vars) <U2 'WS' 'WD'
  * tmodels          (tmodels) <U7 'DTU10MW'
Dimensions without coordinates: turbine
Data variables:
    weight           (state, turbine) float64 dask.array<chunksize=(100, 9), meta=np.ndarray>
    Timeseries_data  (state, Timeseries_vars) float64 dask.array<chunksize=(100, 2), meta=np.ndarray>
    tmodel_sels      (state, turbine, tmodels) bool dask.array<chunksize=(100, 9, 1), meta=np.ndarray>


Output farm variables: AMB_CT, AMB_P, AMB_REWS, AMB_REWS2, AMB_REWS3, AMB_RHO, AMB_TI, AMB_WD, AMB_YAW, CT, D, H, P, REWS, REWS2, REWS3, RHO, TI, WD, X, Y, YAW, order, weight

Chunks: {'state': 500, 'point': 5000}


Calculating 100 states for 9 turbines
[########################################] | 100% Completed | 205.58 ms

Algorithm Iterative: Iteration 1

[########################################] | 100% Completed | 102.70 ms
[########################################] | 100% Completed | 206.25 ms

DefaultConv: Convergence check
  REWS: delta = 1.324e-01, lim = 1.000e-05  --  FAILED
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 9.933e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 2

[########################################] | 100% Completed | 104.03 ms
[########################################] | 100% Completed | 204.68 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 4.966e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 3

[########################################] | 100% Completed | 103.85 ms
[########################################] | 100% Completed | 205.84 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 2.483e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 4

[########################################] | 100% Completed | 102.87 ms
[########################################] | 100% Completed | 207.10 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.242e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 5

[########################################] | 100% Completed | 152.01 ms
[########################################] | 100% Completed | 208.37 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 6.208e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 6

[########################################] | 100% Completed | 102.62 ms
[########################################] | 100% Completed | 207.53 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 3.104e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 7

[########################################] | 100% Completed | 102.84 ms
[########################################] | 100% Completed | 205.69 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.552e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 8

[########################################] | 100% Completed | 103.41 ms
[########################################] | 100% Completed | 205.67 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 7.760e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 9

[########################################] | 100% Completed | 103.23 ms
[########################################] | 100% Completed | 311.10 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 3.880e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 10

[########################################] | 100% Completed | 103.40 ms
[########################################] | 100% Completed | 308.58 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.940e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 11

[########################################] | 100% Completed | 103.47 ms
[########################################] | 100% Completed | 206.75 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 9.700e-07, lim = 1.000e-06  --  OK

Algorithm Iterative: Convergence reached.

Notice the iterations and the convergence behaviour. Now the farm results are ready:

In [4]:
farm_df = farm_results.to_dataframe()
print("\nFarm results data:\n")
print(farm_df[[FV.AMB_REWS, FV.REWS, FV.P]])

Farm results data:

                             AMB_REWS      REWS            P
state               turbine
2023-07-07 12:00:00 0             6.0  6.000000  1532.700000
                    1             6.0  6.000000  1532.700000
                    2             6.0  6.000000  1532.700000
                    3             6.0  5.063574   845.738040
                    4             6.0  5.063574   845.738040
...                               ...       ...          ...
2023-07-07 13:39:00 4             6.0  5.063574   845.738040
                    5             6.0  5.063574   845.738040
                    6             6.0  4.693421   640.016335
                    7             6.0  4.693421   640.016335
                    8             6.0  4.693421   640.016335

[900 rows x 3 columns]

This timeseries has a time step of 1 minute. Let’s visualize the wake dynamics in am animation:

In [5]:
with foxes.utils.runners.DaskRunner() as runner:

    fig, axs = plt.subplots(2, 1, figsize=(5.2,7),
                            gridspec_kw={'height_ratios': [3, 1]})

    anim = foxes.output.Animator(fig)

    # this adds the flow anomation to the upper panel:
    of = foxes.output.FlowPlots2D(algo, farm_results, runner=runner)
    anim.add_generator(
        of.gen_states_fig_xy(
            FV.WS,
            resolution=30,
            quiver_pars=dict(angles="xy", scale_units="xy", scale=0.013),
            quiver_n=35,
            xmax=5000,
            ymax=5000,
            fig=fig,
            ax=axs[0],
            ret_im=True,
            title=None,
            animated=True,
        )
    )

    # This adds the REWS signal animation to the lower panel:
    o = foxes.output.FarmResultsEval(farm_results)
    anim.add_generator(
        o.gen_stdata(
            turbines=[4, 7],
            variable=FV.REWS,
            fig=fig,
            ax=axs[1],
            ret_im=True,
            legloc="upper left",
            animated=True,
        )
    )

    ani = anim.animate()
    plt.close()
    print("done.")

print("Creating animation")
ani
Creating animation data
done.
Creating animation
Out[5]:

For the fun of it, let’s re-run this case assuming the time step was 10 s instead of 1 min. We can do so by using the wake frame Timelines(dt_min=1/6), which is called timelines_10s in the model book:

In [6]:
algo.finalize(clear_mem=True)

algo = foxes.algorithms.Iterative(
    mbook,
    farm,
    states=states,
    rotor_model="centre",
    wake_models=["Bastankhah_linear_k004"],
    wake_frame="timelines_10s",
    partial_wakes_model="auto",
    chunks={FC.STATE: 500, FC.POINT: 5000},
    verbosity=1
)

with foxes.utils.runners.DaskRunner() as runner:

    farm_results = runner.run(algo.calc_farm)

    fig, axs = plt.subplots(2, 1, figsize=(5.2,7),
                            gridspec_kw={'height_ratios': [3, 1]})

    anim = foxes.output.Animator(fig)

    # this adds the flow anomation to the upper panel:
    of = foxes.output.FlowPlots2D(algo, farm_results, runner=runner)
    anim.add_generator(
        of.gen_states_fig_xy(
            FV.WS,
            resolution=30,
            quiver_pars=dict(angles="xy", scale_units="xy", scale=0.013),
            quiver_n=35,
            xmax=5000,
            ymax=5000,
            fig=fig,
            ax=axs[0],
            ret_im=True,
            title=None,
            animated=True,
        )
    )

    # This adds the REWS signal animation to the lower panel:
    o = foxes.output.FarmResultsEval(farm_results)
    anim.add_generator(
        o.gen_stdata(
            turbines=[4, 7],
            variable=FV.REWS,
            fig=fig,
            ax=axs[1],
            ret_im=True,
            legloc="upper left",
            animated=True,
        )
    )

    ani = anim.animate()
    plt.close()
    print("done.")

print("Creating animation")
ani

Algorithm Iterative: Iteration 0


Initializing algorithm 'Iterative'
Initializing model 'Iterative'
Initializing model 'Timeseries'
States 'Timeseries': Reading file /home/jonas/gits/wakes/foxes/foxes/data/states/timeseries_100.csv.gz
Initializing model 'centre'
Initializing model 'basic_ctrl_prer'
Initializing model 'DTU10MW'
Turbine type 'DTU10MW': Reading static data from context 'power_ct_curve'
Path: /home/jonas/gits/wakes/foxes/foxes/data/power_ct_curves/DTU-10MW-D178d3-H119.csv
Initializing model 'basic_ctrl_postr'
Initializing model 'basic_ctrl'
Initializing model 'timelines_10s'
timelines_10s: Pre-calculating ambient wind vectors
Partial wakes 'auto': Applying PartialAxiwake to ['Bastankhah_linear_k004']
Initializing model 'linear'
Initializing model 'Bastankhah_linear_k004'
Initializing model 'PartialAxiwake12'
Initializing model 'auto'

--------------------------------------------------
  Running Iterative: calc_farm
--------------------------------------------------
  n_states : 100
  n_turbines: 9
--------------------------------------------------
  states   : Timeseries (Timeseries)
  rotor    : centre (CentreRotor)
  controller: basic_ctrl (BasicFarmController)
  partialwks: auto (Mapped)
  wake frame: timelines_10s (Timelines)
--------------------------------------------------
  wakes:
    0) Bastankhah_linear_k004 (BastankhahWake)(k=0.04, sp=linear)
--------------------------------------------------
  turbine models:
    0) DTU10MW (PCtFile)
--------------------------------------------------

Initializing model 'SetXYHD1'
Initializing model 'SetXYHD1_t2f'
Initializing model 'calc_yaw_CentreRotor2'
Initializing model 'CalcOrder1'
Initializing model 'SetAmbFarmResults1'
Initializing model 'URelax12_CT'
Initializing model 'FarmWakesCalculation12'
Initializing model 'Iterative_calc'

--------------------------------------------------
  Model oder
--------------------------------------------------
00) SetXYHD1_t2f
01) basic_ctrl
02) calc_yaw_CentreRotor2
03) centre
04) CalcOrder1
05) basic_ctrl
  05.0) Post-rotor: DTU10MW
06) SetAmbFarmResults1
07) FarmWakesCalculation12
--------------------------------------------------

[########################################] | 100% Completed | 102.18 ms

Input data:

 <xarray.Dataset>
Dimensions:          (state: 100, turbine: 9, Timeseries_vars: 2, tmodels: 1)
Coordinates:
  * state            (state) datetime64[ns] 2023-07-07T12:00:00 ... 2023-07-0...
  * Timeseries_vars  (Timeseries_vars) <U2 'WS' 'WD'
  * tmodels          (tmodels) <U7 'DTU10MW'
Dimensions without coordinates: turbine
Data variables:
    weight           (state, turbine) float64 dask.array<chunksize=(100, 9), meta=np.ndarray>
    Timeseries_data  (state, Timeseries_vars) float64 dask.array<chunksize=(100, 2), meta=np.ndarray>
    tmodel_sels      (state, turbine, tmodels) bool dask.array<chunksize=(100, 9, 1), meta=np.ndarray>


Output farm variables: AMB_CT, AMB_P, AMB_REWS, AMB_REWS2, AMB_REWS3, AMB_RHO, AMB_TI, AMB_WD, AMB_YAW, CT, D, H, P, REWS, REWS2, REWS3, RHO, TI, WD, X, Y, YAW, order, weight

Chunks: {'state': 500, 'point': 5000}


Calculating 100 states for 9 turbines
[########################################] | 100% Completed | 309.79 ms

Algorithm Iterative: Iteration 1

[########################################] | 100% Completed | 104.18 ms
[########################################] | 100% Completed | 204.60 ms

DefaultConv: Convergence check
  REWS: delta = 1.324e-01, lim = 1.000e-05  --  FAILED
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 9.933e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 2

[########################################] | 100% Completed | 103.08 ms
[########################################] | 100% Completed | 206.30 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 4.966e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 3

[########################################] | 100% Completed | 103.77 ms
[########################################] | 100% Completed | 207.09 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 2.483e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 4

[########################################] | 100% Completed | 102.23 ms
[########################################] | 100% Completed | 210.96 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.242e-04, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 5

[########################################] | 100% Completed | 102.89 ms
[########################################] | 100% Completed | 210.32 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 6.208e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 6

[########################################] | 100% Completed | 102.62 ms
[########################################] | 100% Completed | 206.35 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 3.104e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 7

[########################################] | 100% Completed | 103.62 ms
[########################################] | 100% Completed | 208.12 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.552e-05, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 8

[########################################] | 100% Completed | 103.24 ms
[########################################] | 100% Completed | 208.72 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 7.760e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 9

[########################################] | 100% Completed | 102.80 ms
[########################################] | 100% Completed | 342.26 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 3.880e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 10

[########################################] | 100% Completed | 103.13 ms
[########################################] | 100% Completed | 205.35 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 1.940e-06, lim = 1.000e-06  --  FAILED

Algorithm Iterative: Iteration 11

[########################################] | 100% Completed | 103.78 ms
[########################################] | 100% Completed | 205.98 ms

DefaultConv: Convergence check
  REWS: delta = 0.000e+00, lim = 1.000e-05  --  OK
  TI  : delta = 0.000e+00, lim = 1.000e-06  --  OK
  CT  : delta = 9.700e-07, lim = 1.000e-06  --  OK

Algorithm Iterative: Convergence reached.

Creating animation data
done.
Creating animation
Out[6]: